package org.jetbrains.plugins.scala.caches

import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.plugins.scala.caches.CachesUtil.CacheCapabilties._
import org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped
import org.jetbrains.plugins.scala.caches.stats.{CacheTracker, Tracer}

import java.util.concurrent.atomic.AtomicReference

// TODO Cache0[R] == CacheN[Unit, R]
private class Cache0[R](id: String, name: String, modificationTracker: => ModificationTracker) {
  // Code generated by @Cached macro annotation, method without parameters

  private[this] val valueAndCounter: AtomicReference[Timestamped[R]] =
    CacheTracker.track(id, name)(new AtomicReference(Timestamped(null.asInstanceOf[R], -1L))) // TODO (timestampedSingleValueCacheCapabilities)

  def apply(f: => R): R = {
    val currModCount = modificationTracker.getModificationCount
    val tracer = Tracer(id, name)
    tracer.invocation()
    val timestamped = valueAndCounter.get
    if (timestamped.modCount == currModCount) {
      timestamped.data
    } else {
      val stackStamp = RecursionManager.markStack()
      tracer.calculationStart()
      val computed = try {
        f
      } finally {
        tracer.calculationEnd()
      }
      if (stackStamp.mayCacheNow()) {
        valueAndCounter.compareAndSet(timestamped, Timestamped(computed, currModCount))
        valueAndCounter.get.data
      } else {
        computed
      }
    }
  }

/*
  @Cached(ModTracker.anyScalaPsiChange)
  def foo(): String = "Foo"

  new _root_.scala.volatile();
  private val org$example$Example$foo$$valueAndCounter: _root_.java.util.concurrent.atomic.AtomicReference[_root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped[String]] = {
    import _root_.org.jetbrains.plugins.scala.caches.CachesUtil.CacheCapabilties._;
    _root_.org.jetbrains.plugins.scala.caches.stats.CacheTracker.track("org$example$Example$foo$cacheKey", "Example.foo")(new _root_.java.util.concurrent.atomic.AtomicReference(_root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped(null, -1L)))
  };
  def foo(): String = {
    def org$example$Example$foo$$cachedFun(): String = "Foo";
    val currModCount = ModTracker.anyScalaPsiChange.getModificationCount();
    val org$example$Example$foo$$tracer = _root_.org.jetbrains.plugins.scala.caches.stats.Tracer("org$example$Example$foo$cacheKey", "Example.foo");
    org$example$Example$foo$$tracer.invocation();
    val timestamped = org$example$Example$foo$$valueAndCounter.get;
    if (timestamped.modCount.$eq$eq(currModCount))
      timestamped.data
    else
      {
        val stackStamp = org.jetbrains.plugins.scala.caches.RecursionManager.markStack();
        org$example$Example$foo$$tracer.calculationStart();
        val computed = try {
          org$example$Example$foo$$cachedFun()
        } finally org$example$Example$foo$$tracer.calculationEnd();
        if (stackStamp.mayCacheNow())
          {
            org$example$Example$foo$$valueAndCounter.compareAndSet(timestamped, _root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped(computed, currModCount));
            org$example$Example$foo$$valueAndCounter.get.data
          }
        else
          computed
      }
  };
  ()
*/
}

